// primitives.hpp : Primitives. Intersection and surface normal evaluation
//

#ifndef PRIMITIVES_HPP_INCLUDE
#define PRIMITIVES_HPP_INCLUDE

#include <vector>

#include "types.hpp"
#include "renderer.hpp"

class Renderer;
class Scene;
struct SampleInfo;
class Shader;
class SurfaceShader;

class Primitive
{
protected:
//	Primitive();
public:
	enum Intersection {NO_INTERSECTION = 0, FRONT_INTERSECTION = 1, BACK_INTERSECTION = 2};
	virtual bool intersect(const Ray& ray, double dist, SampleInfo& sample) const = 0;
	virtual Color shade(const SampleInfo& sample, const Scene& scene, const Renderer& renderer) const = 0;
	virtual const char* getType() const = 0;
};

class SurfacePrimitive : public Primitive
{
protected:
	const SurfaceShader* frontShader_, * backShader_;
public:
	enum NormalType {RAW_NORMAL, SMOOTHED_NORMAL};
	SurfacePrimitive();
	void setSurfaceShader(const SurfaceShader *shader);
	Color shade(const SampleInfo& sample, const Scene& scene, const Renderer& renderer) const;
	virtual Vector3 getNormal(const SampleInfo& sample, SurfacePrimitive::NormalType type) const = 0;
	virtual Vector3 getUVW(const SampleInfo& sample) const = 0;
};

class Sphere : public SurfacePrimitive
{
private:
	Vector3 origin_;
	double radius_;
public:
	Sphere(const Vector3& origin, double radius);

	bool intersect(const Ray& ray, double dist, SampleInfo& sample) const;
	Vector3 getNormal(const SampleInfo& sample, SurfacePrimitive::NormalType type) const;
	Vector3 getUVW(const SampleInfo& sample) const;
	const char* getType() const;
};

class Plane : public SurfacePrimitive
{
private:
	Vector3 origin_;
	Vector3 normal_;
public:
	Plane(const Vector3& origin, const Vector3& normal);

	bool intersect(const Ray& ray, double dist, SampleInfo& sample) const;
	Vector3 getNormal(const SampleInfo& sample, SurfacePrimitive::NormalType type) const;
	Vector3 getUVW(const SampleInfo& sample) const;
	const char* getType() const;
};

struct TriIDs
{
	int id0, id1, id2;
	TriIDs(int i0, int i1, int i2) : id0(i0), id1(i1), id2(i2) {}
};

struct TriBuffer
{
	vector<TriIDs> vertIDs;
	vector<Vector3> vertices;
	vector<TriIDs> normIDs;
	vector<Vector3> normals;
	vector<vector<TriIDs> > uvwIDs;
	vector<vector<Vector3> > uvws;
};

class Triangle : public SurfacePrimitive
{
private:
	Vector3 origin_;
	Vector3 normal_;
	Matrix3 m_;

	int triID_;
	const TriBuffer * buf_;

	bool innerIntersect(const Vector3& position, Vector3& weights) const;

public:
	Triangle::Triangle(int triID, const TriBuffer* buf);
	bool intersect(const Ray& ray, double dist, SampleInfo& sample) const;
	Vector3 getNormal(const SampleInfo& sample, SurfacePrimitive::NormalType type) const;
	Vector3 getUVW(const SampleInfo& sample) const;
	const char* getType() const;
};

#endif //PRIMITIVES_HPP_INCLUDE
